/**
 * puppeteer-extra和playwright-extra是社区构建的库，它们引入了插件系统来分别增强puppeteer和 的 使用playwright（引入额外的功能，例如通过使用puppeteer-extra-plugin-stealth插件来提高隐秘性）。
 * 在此示例中，我们将向您展示如何使用该puppeteer-extra-plugin-stealth插件来帮助您在抓取目标网站时避免机器人检测。
 * 确保您已通过首选软件包管理器安装了playwright-extra和软件包puppeteer-extra-plugin-stealth
 * npm install playwright-extra puppeteer-extra-plugin-stealth
 */
import { Dataset, PlaywrightCrawler, log as logg } from 'crawlee';
// 对于 playwright-extra，您需要导入您要使用的浏览器类型本身！
// 默认情况下，PlaywrightCrawler 使用 chromium，但您也可以使用 firefox 或 webkit。
import { chromium } from 'playwright-extra';
import stealthPlugin from 'puppeteer-extra-plugin-stealth';

// 首先，我们告诉其他剧作家使用我们想要的插件。
// 某些插件可能有你可以提交的选项——阅读它们的文档！
chromium.use(stealthPlugin());

// 创建PlaywrightCrawler类的实例-爬网程序
// 它自动加载无头Chrome/Playwright中的URL。
const crawler = new PlaywrightCrawler({
    launchContext: {
        // ！！！你需要指定这个选项来告诉Crawlee使用额外的戏剧作家作为启动器！！！
        launcher: chromium,
        launchOptions: {
            // 其他剧作家选项照常工作
            headless: true,
        },
    },

    // 几页后停止抓取
    maxRequestsPerCrawl: 50,

    // 每个要爬网的URL都会调用此函数。
    // 在这里，您可以编写您熟悉的木偶戏脚本，
    // 除了由Crawlee自动管理的浏览器和页面之外。
    // 该函数接受单个参数，该参数是一个具有以下字段的对象：
    // -request：包含URL和HTTP方法等信息的request类的实例
    // -page：Puppeteer的页面对象（请参见https://pptr.dev/#show=api-课堂页面）
    async requestHandler({ request, page, enqueueLinks, log }) {
        log.info(`Processing ${request.url}...`);

        // 由 Puppeteer 在浏览器上下文中评估的函数。
        const data = await page.$$eval('.athing', ($posts) => {
            const scrapedData: { title: string; rank: string; href: string }[] = [];

            // 我们正在获取 Hacker News 上每个帖子的标题、排名和 URL。
            $posts.forEach(($post) => {
                scrapedData.push({
                    title: $post.querySelector('.title a').innerText,
                    rank: $post.querySelector('.rank').innerText,
                    href: $post.querySelector('.title a').href,
                });
            });

            return scrapedData;
        });

        // 将结果存储到默认数据集。
        await Dataset.pushData(data);

        // 找到下一页的链接，如果存在则将其加入队列。
        const infos = await enqueueLinks({
            selector: '.morelink',
        });

        if (infos.processedRequests.length === 0) log.info(`${request.url} is the last page!`);
    },

    // 如果页面处理失败超过 maxRequestRetries+1 次，则调用此函数。
    failedRequestHandler({ request, log }) {
        log.error(`Request ${request.url} failed too many times.`);
    },
});

await crawler.addRequests(['https://news.ycombinator.com/']);

// 运行爬虫并等待其完成。
await crawler.run();

logg.info('爬虫完成!');
